using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Furion.ClayObject.Extensions;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using WebSMS.Core.ResponseEntity;

namespace WebSMS;

public class UserService : ITerUser, ITransient
{
    private readonly IRepository<UserPo> _repoUsers;
    private readonly IRepository<UserRuleLinks> _repoUsersRuleLinks;
    private readonly IRepository<RulePo> _repoRule;
    private readonly IRepository<UserMenuAuthorityLinks> _userMenuAuthorityLinks;
    private readonly IRepository<MenuPo> _menuRepository;
    private readonly IRepository<Department> _departmentRepository;
    private readonly IRepository<PostPo> _postRepository;
    private readonly IRepository<UserPostLinks> _userPostRepository;

    public UserService(IRepository<UserPo> repository,
        IRepository<UserRuleLinks> repoUsersRuleLinks,
        IRepository<RulePo> repoRule,
        IRepository<UserMenuAuthorityLinks> userMenuAuthorityLinks,
        IRepository<MenuPo> menuRepository,
        IRepository<Department> departmentRepository,
        IRepository<PostPo> postRepository,
        IRepository<UserPostLinks> userPostRepository)
    {
        _repoUsers = repository;
        _repoUsersRuleLinks = repoUsersRuleLinks;
        _repoRule = repoRule;
        _userMenuAuthorityLinks = userMenuAuthorityLinks;
        _menuRepository = menuRepository;
        _departmentRepository = departmentRepository;
        _postRepository = postRepository;
        _userPostRepository = userPostRepository;
    }

    public async Task<UserPo> Login(string username, string password)
    {
        var user = await _repoUsers.FirstOrDefaultAsync(u => u.Username == username && u.Password == password);
        if (user != null)
        {
            return user;
        }

        return null;
    }

    public async Task<bool> BannedUserById(Guid userId)
    {
        var user = await _repoUsers.FirstOrDefaultAsync(u => u.Id == userId);
        if (user != null)
        {
            user.IsActive = true;
            user.BanDate = DateTime.Now.ToString();
            var e = await _repoUsers.UpdateNowAsync(user);
            return true;
        }

        return false;
    }

    public async Task<bool> EnableUser(Guid userId)
    {
        var user = await _repoUsers.FirstOrDefaultAsync(u => u.Id == userId);
        if (user != null)
        {
            user.IsActive = false;
            user.BanDate = "";
             await _repoUsers.UpdateNowAsync(user);
            return true;
        }

        return false;
    }

    public async Task<bool> UpdateUser(UserPo userPo)
    {
        await _repoUsers.UpdateNowAsync(userPo);
        return true;
    }

    public async Task<bool> DeleteUser(Guid userId)
    {
        var user = await _repoUsers.FirstOrDefaultAsync(u => u.Id == userId);
        if (user != null)
        {
            user.IsDeleted = true;
            user.DeleteDate = DateTime.Now.ToString();
            var e = await _repoUsers.UpdateNowAsync(user);
            return true;
        }

        return false;
    }

    public async Task<List<UserRuleResponse>>  GetUserList(string username,int page, int size)
    {
        var query = from user in _repoUsers.Where(u => !u.IsDeleted && u.Username != username)
            join link in _repoUsersRuleLinks.AsQueryable() on user.Id equals link.UserId into userRules
            from userRuleLink in userRules.DefaultIfEmpty()
            join rule in _repoRule.AsQueryable() on userRuleLink.RuleId equals rule.Id into rules
            from userRule in rules.DefaultIfEmpty()
            group userRule by new
            {
                user.Id,
                user.Username,
                user.Email,
                user.Phone,
                user.IsActive,
                user.BanDate,
                user.CreateDate,    
                user.DeleteDate
            } into g
            select new UserRuleResponse
            {
                UserId = g.Key.Id,
                Username = g.Key.Username,
                Email = g.Key.Email,
                Phone = g.Key.Phone,
                IsActive = g.Key.IsActive,
                BanDate = g.Key.BanDate,
                CreateDate = g.Key.CreateDate,
                DeleteDate = g.Key.DeleteDate,
                Rules = g.Where(rule => rule != null).ToList()
            };

        return  query.ToList();
    }

    public async Task<PagedList<UserPo>> GetRecoveryUserList(int page, int size)
    {
        var pagedList = await _repoUsers.Where(u => u.IsDeleted).ToPagedListAsync(page, size);
        return pagedList;
    }

    public async Task<bool> RecoveryUser(Guid userId)
    {
        var userPos = await _repoUsers.FirstOrDefaultAsync(u => u.Id == userId);
        if (userPos != null)
        {
            userPos.IsDeleted = false;
            userPos.DeleteDate = "";
            await _repoUsers.UpdateNowAsync(userPos);
            return true;
        }
        return false;
    }

    public async Task<PagedList<UserPo>> AddUser(UserPo user, int page, int size)
    {
        await _repoUsers.InsertNowAsync(user);
        return await _repoUsers.Where(u => !u.IsActive).ToPagedListAsync(1, 10);
    }

    public int GetUserCount()
    {
        return  _repoUsers.Count(u => u.IsDeleted == false);
    }
    

    public IQueryable GetUserGroupByCreateDate()
    {
        var create = from u in _repoUsers.AsQueryable()
            group u by u.CreateDate
            into dateGroup
            select new
            {
                date = dateGroup.Key,
                value = dateGroup.Count()
            }.ToDictionary();;
        
        return create;
    }

    public IQueryable GetUserGroupByBanDate()
    {
        var ban = from u in _repoUsers.Where(u => u.BanDate != "")
            group u by u.BanDate
            into dateGroup
            select new
            {
                date = dateGroup.Key,
                value = dateGroup.Count()
            }.ToDictionary();
        return ban;
    }

    public IQueryable GetUserGroupByDelDate()
    {
        var del = from u in _repoUsers.Where(u =>  u.DeleteDate != "")
            group u by u.DeleteDate
            into dateGroup
            select new
            {
                date = dateGroup.Key,
                value = dateGroup.Count()
            }.ToDictionary();;

        return del;
    }

    public async Task<bool> VerifyUserPassword(string username, string password)
    {
        var user = await _repoUsers.FirstOrDefaultAsync(u => u.Username == username && u.Password == PasswordMd5.md5(password));
        return user!= null;
    }

    public async Task<IQueryable> GetUserMenuList()
    {
        var query = from user in _repoUsers.Where(u => !u.IsDeleted)
            join link in _userMenuAuthorityLinks.AsQueryable()
                on user.Id equals link.UserId into userLinks
            from link in userLinks.DefaultIfEmpty()
            join menu in _menuRepository.AsQueryable()
                on link.MenuId equals menu.Id into userMenus
            from menu in userMenus.DefaultIfEmpty()
            group menu by new 
            {
                user.Id,
                user.Username,
                user.Password,
                user.Email,
                user.Phone,
                user.IsActive,
                user.IsDeleted
            } into userGroup
            select new UserMenusResponse
            {
                Id = userGroup.Key.Id,
                Username = userGroup.Key.Username,
                Password = userGroup.Key.Password,
                Email = userGroup.Key.Email,
                Phone = userGroup.Key.Phone,
                IsActive = userGroup.Key.IsActive,
                IsDeleted = userGroup.Key.IsDeleted,
                MenuList = userGroup.Where(m => m != null).ToList()
            };

        return query;
    }

    public async Task<bool> AddUserMenu(Guid userId, Guid menuId)
    {
       await _userMenuAuthorityLinks.InsertNowAsync(new UserMenuAuthorityLinks() { Id = Guid.NewGuid(), MenuId = menuId, UserId = userId});
       return true;
    }

    public async Task<bool> DeleteUserMenu(Guid userId)
    {
        var enumerable = _userMenuAuthorityLinks.Where(um => um.UserId == userId);
        await _userMenuAuthorityLinks.DeleteNowAsync(enumerable);
        return true;
    }

    public async Task<IQueryable> GetUserDetails(Guid userId)
    {
    var query = from user in _repoUsers.Where(u => u.Id == userId)
        join userPostLink in _userPostRepository.AsQueryable() on user.Id equals userPostLink.UserId into userPosts
        from userPostLink in userPosts.DefaultIfEmpty()
        join post in _postRepository.AsQueryable() on userPostLink.PostId equals post.Id into posts
        from userPost in posts.DefaultIfEmpty()
        join department in _departmentRepository.AsQueryable() on userPost.DepartmentId equals department.Id into departments
        from userDepartment in departments.DefaultIfEmpty()
        join parentDepartment in _departmentRepository.AsQueryable() on userDepartment.ParentId equals parentDepartment.Id into parentDepartments
        from parentDept in parentDepartments.DefaultIfEmpty()
        join link in _repoUsersRuleLinks.AsQueryable() on user.Id equals link.UserId into userRules
        from userRuleLink in userRules.DefaultIfEmpty()
        join rule in _repoRule.AsQueryable() on userRuleLink.RuleId equals rule.Id into rules
        from userRule in rules.DefaultIfEmpty()
        group new { user, userRule, userPost, userDepartment, parentDept } by new
        {
            user.Id,
            user.Username,
            user.Email,
            user.Phone,
            user.IsActive,
            user.BanDate,
            user.CreateDate,
            user.DeleteDate
        } into g
        select new
        {
            UserId = g.Key.Id,
            Username = g.Key.Username,
            Email = g.Key.Email,
            Phone = g.Key.Phone,
            IsActive = g.Key.IsActive,
            BanDate = g.Key.BanDate,
            CreateDate = g.Key.CreateDate,
            DeleteDate = g.Key.DeleteDate,
            Rules = g.Where(x => x.userRule != null).Select(x => x.userRule).ToList(),
            Post = g.Select(x => x.userPost).FirstOrDefault(),
            Department = g.Select(x => new
            {
                x.userDepartment,
                ParentDepartment = x.parentDept
            }).FirstOrDefault()
        };

    var result = query.ToList();
    return query;
    }

}